package com.codejoys.monitor.schedule;

import com.codejoys.monitor.Constants;
import com.codejoys.monitor.domain.ChannelConfig;
import com.codejoys.monitor.dto.ChannelConfigMap;
import com.codejoys.monitor.dto.DeviceDto;
import com.codejoys.monitor.event.AlarmEvent;
import com.codejoys.monitor.event.CheckTemperatureEvent;
import com.codejoys.monitor.exception.SysException;
import com.codejoys.monitor.jna.CANLib;
import com.codejoys.monitor.mapper.DeviceMapper;
import com.codejoys.monitor.service.StatusService;
import com.codejoys.monitor.service.impl.ModbusServiceImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.net.Socket;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;

@Component
public class DataScanSchedule {
    private static final Logger logger = LoggerFactory.getLogger(SysSchedule.class);

    ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(5);

    @Autowired
    StatusService statusService;

    @Autowired
    DeviceMapper deviceMapper;

    @Autowired
    ChannelConfigMap configMap;

    @Autowired
    CANLib canLib;

    @Autowired
    ApplicationContext applicationContext;

    // 5分钟检查一次数据错误
    @Scheduled(fixedRateString = "60000", initialDelay = 500)
    public void checkData() {
        logger.info("扫描错误数据中。。。");

        // 检查是否有超过阈值的设备（此检查会忽略开机5分钟左右的检测，具体时间可以设置）
        final List<DeviceDto> devices = statusService.findDevicesWithErrorData();

        if (devices == null || devices.size() == 0) return;

        logger.error("设备数据异常：{}", devices);

        devices.forEach(d -> {
            threadPool.submit(() -> {
                onDeviceDataError(d);
            });
        });
    }

    // 5秒钟检查一次温度
    @Scheduled(fixedRateString = "5000", initialDelay = 500)
    public void checkTemperature() {
        applicationContext.publishEvent(new CheckTemperatureEvent(this));
    }

    public void onDeviceDataError(DeviceDto device) {
        final ChannelConfig config = configMap.getConfigs().get(device.getChannel());

        int canIndex = config.getCanIndex();
        int moduleId = config.getCanModuleId();

        int status = canLib.GetModuleStatus(canIndex, 0, 1, moduleId);

        boolean powerOff = ((status & 0x2000000) == Constants.POWER_ON_STATE);
        boolean hasError = (Constants.ERROR & status) != 0;

        logger.info("设备状态，{} 是否关机：{}, 是否出错：{}",
                Integer.toBinaryString(status), powerOff, hasError);

        // 关闭设备
        int result = canLib.TurnOnOffModule(canIndex, 0, (byte) 1, moduleId, 0);

        if (result != 1) {
            logger.error("模块关闭失败！。。{} result: {}", config.getChannel(), result);
        } else {
            logger.info("设备{}关闭成功！", config.getChannel());
        }

        device.setStatus((byte) Constants.Status.ERROR.value());
        device.setAgingStopTime(new Date());
        device.setUpdated(new Date());
        device.setAgingResult((byte) 2);        // 老化异常
        device.setNeedRemark((byte) 1); // 需要设置错误备注

//        String message = "电流电压超过阈值，自动关机！";
//        if (device.getAgingErrorRemark() == null) {
//            device.setAgingErrorRemark(message);
//        } else {
//            device.setAgingErrorRemark(device.getAgingErrorRemark() + "\n" + message);
//        }
        deviceMapper.updateByPrimaryKeySelective(device);

        // 等待设备关闭
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 关闭电源
        try (Socket socket = ModbusServiceImpl.connect(config.getCabinetIp())) {
            ModbusServiceImpl.toggleOpen(socket, config, false);
        } catch (IOException e) {
            logger.error("socket连接出错！", e);

            throw new SysException(2003, "socket连接出错！自动停止失败。");
        }

        // 发送报警
        logger.warn("设备数据异常，发送报警！");
        applicationContext.publishEvent(new AlarmEvent(this, device));
    }
}
